/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 adventure_spell_effect.cpp

	$Header: /heroes4/adventure_spell_effect.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#include "adventure_spell_effect.h"

#include "adventure_map.h"
#include "adventure_tile_size.h"
#include "animation.h"
#include "animation_cache.h"
#include "army.h"
#include "float_object.h"
#include "game_time.h"
#include "override_input_handler.h"
#include "platform.h"
#include "uncopyable.h"

// --------------------------------------------------------------------------
// Unnamed namespace
// --------------------------------------------------------------------------

namespace
{

	// --------------------------------------------------------------------------
	// t_object_placer class
	// --------------------------------------------------------------------------

	class t_object_placer : private t_uncopyable
	{
	public:
		// Constructor/Destructor
		t_object_placer(
			t_adventure_map &		map,
			t_adventure_object &	object,
			t_adv_map_point const &	position );
		~t_object_placer();

	private:
		// Data members
		t_adventure_map &		m_map;
		t_adventure_object &	m_object;
	};

	// --------------------------------------------------------------------------
	// t_object_placer members
	// --------------------------------------------------------------------------

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_object_placer::t_object_placer(
		t_adventure_map &		map,
		t_adventure_object &	object,
		t_adv_map_point const &	position )
		:	m_map( map ),
			m_object( object )
	{
		m_object.place( m_map, position );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_object_placer::~t_object_placer()
	{
		m_map.remove_object( &m_object );
	}

	// --------------------------------------------------------------------------
	// t_idle_processing_resumer class
	// --------------------------------------------------------------------------

	class t_idle_processing_resumer : private t_uncopyable
	{
	public:
		// Constructor/Destructor
		explicit t_idle_processing_resumer( t_idle_processor & idler );
		~t_idle_processing_resumer();

	private:
		// Data members
		t_idle_processor &	m_idler;
	};

	// --------------------------------------------------------------------------
	// t_idle_processing_resumer members
	// --------------------------------------------------------------------------

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_idle_processing_resumer::t_idle_processing_resumer(
		t_idle_processor &	idler )
		:	m_idler( idler )
	{
		m_idler.resume_idle_processing();
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_idle_processing_resumer::~t_idle_processing_resumer()
	{
		m_idler.suspend_idle_processing();
	}

	// --------------------------------------------------------------------------
	// Non-member functions
	// --------------------------------------------------------------------------

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_screen_point get_base_point( t_animation const & animation )
	{
		t_screen_point result = animation.get_base_point();
		if ( !animation.x_is_valid() || !animation.y_is_valid() )
		{
			t_screen_rect rect = animation.get_rect();
			if ( !animation.x_is_valid() )
				result.x = ( rect.left + rect.right ) / 2;
			if ( !animation.y_is_valid() )
				result.y = ( rect.top + rect.bottom ) / 2;
		}
		return result;
	}

} // Unnamed namespace

// --------------------------------------------------------------------------
// t_adventure_spell_effect members
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_adventure_spell_effect::t_adventure_spell_effect(
	t_adventure_object const &	target,
	bool						underlay )
	:	m_done( true ),
		m_target( target ),
		m_underlay( underlay )
{
	set_event_recordable( false );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_adventure_spell_effect::accept( t_visitor & visitor )
{
	assert( false );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_adventure_spell_effect::accept( t_visitor & visitor ) const
{
	assert( false );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_adventure_spell_effect::animates() const
{
	return false;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_adventure_spell_effect::t_clone_ptr t_adventure_spell_effect::clone() const
{
	assert( false );
	return t_clone_ptr();
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_adventure_spell_effect::draw_shadow_to(
	t_uint32				current_time,
	t_screen_rect const &	source_rect,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point ) const
{
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_adventure_spell_effect::draw_subimage_to(
	int						subimage_num,
	t_uint32				current_time,
	t_screen_rect const &	source_rect, 
	t_abstract_bitmap16 &	dest, 
	t_screen_point const &	dest_point,
	int						alpha ) const
{
	assert( subimage_num == 0 );
	draw_to( current_time, source_rect, dest, dest_point );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_footprint const &	t_adventure_spell_effect::get_footprint() const
{
	return *this;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
std::string t_adventure_spell_effect::get_name() const
{
	return std::string();
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_screen_rect t_adventure_spell_effect::get_rect(
	t_uint32	current_time ) const
{
	return get_rect();
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_screen_rect t_adventure_spell_effect::get_shadow_rect() const
{
	return t_screen_rect( 0, 0, 0, 0 );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_screen_rect t_adventure_spell_effect::get_shadow_rect(
	t_uint32	current_time ) const
{
	return get_shadow_rect();
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
int t_adventure_spell_effect::get_subimage_count() const
{
	return 1;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
int t_adventure_spell_effect::get_subimage_depth_offset(
	int	subimage_num ) const
{
	assert( subimage_num == 0 );
	return -1;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_screen_rect t_adventure_spell_effect::get_subimage_rect(
	int	subimage_num ) const
{
	assert( subimage_num == 0 );
	return get_rect();
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_screen_rect t_adventure_spell_effect::get_subimage_rect(
	int			subimage_num,
	t_uint32	time ) const
{
	assert( subimage_num == 0 );
	return get_rect( time );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_adventure_spell_effect::hit_test(
	t_uint32				current_time,
	t_screen_point const &	point ) const
{
	return false;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_adventure_spell_effect::needs_redrawing(
	t_uint32	last_update_time,
	t_uint32	current_time ) const
{
	return false;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_adventure_spell_effect::run_modal()
{
	assert( m_target.is_on_map() );

	t_adventure_map & map = *m_target.get_map();
	t_adv_map_point position = m_target.get_position();
	t_object_placer object_placer( map, *this, position );

	t_override_input input_overrider;

	m_done = false;
	while ( !m_done )
		process_message();
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_adventure_spell_effect::subimage_animates( int subimage_num ) const
{
	assert( subimage_num == 0 );
	return animates();
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_adventure_spell_effect::subimage_is_underlay( int subimage_num ) const
{
	assert( subimage_num == 0 );
	return m_underlay;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_adventure_spell_effect::subimage_needs_redrawing(
	int			subimage_num,
	t_uint32	last_update_time,
	t_uint32	current_time ) const
{
	assert( subimage_num == 0 );
	return needs_redrawing( last_update_time, current_time );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_adventure_spell_effect::write_object( std::streambuf & stream ) const
{
	assert( false );
	return true;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_map_point_2d t_adventure_spell_effect::get_size() const
{
	return t_map_point_2d( 1, 1 );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_adventure_spell_effect::is_cell_flat(
	t_map_point_2d const &	point ) const
{
	return false;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_adventure_spell_effect::is_cell_impassable(
	t_map_point_2d const &	point ) const
{
	return false;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_adventure_spell_effect::is_left_edge_trigger(
	t_map_point_2d const &	point ) const
{
	return false;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_adventure_spell_effect::is_right_edge_trigger(
	t_map_point_2d const &	point ) const
{
	return false;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_adventure_spell_effect::is_trigger_cell(
	t_map_point_2d const &	point ) const
{
	return false;
}

// --------------------------------------------------------------------------
// t_adventure_animation_spell_effect members
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_adventure_animation_spell_effect::t_adventure_animation_spell_effect(
	t_adventure_object const &	target,
	bool						underlay,
	t_animation_ptr				animation_ptr )
	:	t_adventure_spell_effect( target, underlay ),
		t_idle_processor( 1 ),
		m_animation_ptr( animation_ptr ),
		m_cur_frame_num( 0 )
{
	assert( m_animation_ptr.get() != 0 );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_adventure_animation_spell_effect::draw_to(
	t_uint32				current_time, 
	t_screen_rect const &	source_rect,
	t_abstract_bitmap16 &	dest, 
	t_screen_point const &	dest_point ) const
{
	if ( m_cur_frame_num < 0 || m_cur_frame_num >= m_animation_ptr->get_frame_count() )
		return;

	t_bitmap_layer const * frame_ptr = m_animation_ptr->get_frame( m_cur_frame_num );
	if ( frame_ptr == 0 )
		return;

	t_screen_point base_point = get_base_point( *m_animation_ptr );
	base_point.y -= k_adventure_tile_height / 2;

	frame_ptr->draw_to( source_rect + base_point, dest, dest_point + source_rect.top_left() );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_screen_rect t_adventure_animation_spell_effect::get_rect() const
{
	t_screen_point base_point = get_base_point( *m_animation_ptr );
	base_point.y -= k_adventure_tile_height / 2;
	return m_animation_ptr->get_rect() - base_point;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_adventure_animation_spell_effect::run_modal()
{
	m_cur_frame_num = 0;
	m_start_time = ::get_time();

	t_idle_processing_resumer resumer( *this );

	t_adventure_spell_effect::run_modal();
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_adventure_animation_spell_effect::on_idle()
{
	if ( m_cur_frame_num >= m_animation_ptr->get_frame_count() )
		return;

	t_uint32 elapsed_time = ::get_time() - m_start_time;
	int frame_num = elapsed_time / m_animation_ptr->get_frame_delay();
	if ( frame_num != m_cur_frame_num )
	{
		t_float_object floater( this );
		m_cur_frame_num = frame_num;
		if ( m_cur_frame_num >= m_animation_ptr->get_frame_count() )
			finish();
	}
}

// -----------------------------------------------------------------
// t_adv_magic_resistance_effect class
// -----------------------------------------------------------------

t_adv_magic_resistance_effect::t_adv_magic_resistance_effect( t_army const & army )
:	t_adventure_animation_spell_effect( army, false, get_animation() )
{
}

t_animation_ptr t_adv_magic_resistance_effect::get_animation()
{
	static t_animation_cache cache( "spell.adventure.magic resistance" );
	return cache.get();
}

// -----------------------------------------------------------------
// t_adv_mire_effect class
// -----------------------------------------------------------------

t_adv_mire_effect::t_adv_mire_effect( t_army const & army )
:	t_adventure_animation_spell_effect( army, true, get_animation() )
{
}

t_animation_ptr t_adv_mire_effect::get_animation()
{
	static t_animation_cache cache( "spell.adventure.mire" );
	return cache.get();
}
